/*
 * userClerk.h
 *
 *  Created on: Jun 16, 2011
 *      Author: peckb1
 */

#ifndef USERCLERK_H_
#define USERCLERK_H_

#include "syscall.h"

#include "userItem.h"
#include "userLocks.h"
#include "userCashRegister.h"

typedef struct Clerk {
    int haveAJob;
    int currentRegister;

} Clerk;

void
ClerkGoToWork(int lineId) {
	int clerkLock;
	int registerLock;
	int registerStatusLock;
	int peopleWaiting;
	int totalItemsProvidedCount;
	int itemIdpassed;
	int totalTransactionAmount;
	int currentRegister;
	int customerWantsToReturn;
	int waitForClerksToReturn;
	int totalSignaled;
	Clerk clerk;
    clerk.haveAJob = TRUE;
    clerk.currentRegister = lineId;
    currentRegister = lineId;
    while(clerk.haveAJob == TRUE) {
        /* Make sure no one can get in line while we help our customer */
        #ifdef PRINT_LOCKS_CLERK
            printf_3("CLERK# ", currentRegister, " clerkLineLock->Acquire()\n");
        #endif

        Acquire(theLocksInHomeDepotEntry._clerkLinelock);

        #ifdef PRINT_LOCKS_CLERK
            printf_3("CLERK# ", currentRegister, " clerkLineLock->Acquire() Done!\n");
        #endif

        if(theLocksInHomeDepotEntry._clerkLineLength[currentRegister] > 0) {
            #ifdef PRINT_EXTRA_LOG_CLERK
                printf_5("CLERK# ", currentRegister, " : founds out there are ", theLocksInHomeDepotEntry._clerkLineLength[currentRegister], " customers waiting for him \n");
            #endif

            /* I have some customers in my line */
            if(theLocksInHomeDepotEntry._clerkLineLength[currentRegister] > 4) {
                #ifdef PRINT_LOCKS_CLERK
                    printf_3("Clerk [", currentRegister, "] see more than 4 people in line and calls Manager.\n");
                #endif
                #ifdef PRINT_LOCKS_CLERK
                    printf_3("CLERK# ", currentRegister, " _signalManagerClerkHelp->Acquire()\n");
                #endif

                Acquire(theLocksInHomeDepotEntry._signalManagerClerkHelp);

                #ifdef PRINT_LOCKS_CLERK
                    printf_3("CLERK# ", currentRegister, " _signalManagerClerkHelp->Acquire() Done!\n");
                #endif

                theLocksInHomeDepotEntry._storeNeedsMoreClerks[currentRegister] = TRUE;

                #ifdef PRINT_LOCKS_CLERK
                    printf_3("CLERK# ", currentRegister, " _signalManagerClerkHelp->Release()\n");
                #endif

                Release(theLocksInHomeDepotEntry._signalManagerClerkHelp);

                #ifdef PRINT_LOCKS_CLERK
                    printf_3("CLERK# ", currentRegister, " _signalManagerClerkHelp->Release() Done!\n");
                #endif
            }

            #ifdef PRINT_LOCKS_CLERK
                printf_5("CLERK# ", currentRegister, " _clerkLine[", currentRegister, "][clerkLineLock]->Signal()\n");
            #endif

            Signal(theLocksInHomeDepotEntry._clerkLineCV[currentRegister], theLocksInHomeDepotEntry._clerkLinelock);
            (theLocksInHomeDepotEntry._clerkLineLength[currentRegister])--;
            clerkLock = theLocksInHomeDepotEntry._clerkLock[currentRegister];

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLock->Acquire()\n");
            #endif

            Acquire(clerkLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLock->Acquire() Done!\n");
            #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLineLock->Release()\n");
            #endif

            Release(theLocksInHomeDepotEntry._clerkLinelock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLineLock->Release() Done!\n");
            #endif

            /* The customer is in sleep, waiting for our signal so he can */
            /* provide his information (returning or purchase) */
            #ifdef PRINT_EXTRA_LOG_CLERK
                printf_3("CLERK# ", currentRegister, " : Waits till customer tell him he is ready to pass the items\n");
            #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_5("CLERK# ", currentRegister, " clerkCV[", currentRegister, "][clerkLock]->Wait()\n");
            #endif

            Wait(theLocksInHomeDepotEntry._clerkCV[currentRegister], clerkLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_5("CLERK# ", currentRegister, " clerkCV[", currentRegister, "][clerkLock]->Wait() Done!\n");
            #endif

            /* Customer just signaled us that he is ready to pass */
            /* the items to us */
            #ifdef PRINT_EXTRA_LOG_CLERK
                printf_3("CLERK# ", currentRegister, " : Customer announced he is read to pass the items\n");
            #endif

            Acquire(theLocksInHomeDepotEntry._customerActionLock);
            customerWantsToReturn = theLocksInHomeDepotEntry._customerWantsToScanItems[currentRegister];
            Release(theLocksInHomeDepotEntry._customerActionLock);


            /* HelpCustomerCheckout(clerk, customerWantsToReturn); */
                currentRegister = clerk.currentRegister;

                clerkLock = theLocksInHomeDepotEntry._clerkLock[currentRegister];
                registerLock = theLocksInHomeDepotEntry._fromRegisterInteractionLock[currentRegister];
                registerStatusLock = theLocksInHomeDepotEntry._registerLock[currentRegister];
                peopleWaiting = theLocksInHomeDepotEntry._peopleWaitingForCashRegiterLock[currentRegister];

                #ifndef IGNORE_REGISTERS
                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " :Lets acquire the register\n");
                    #endif

                    Acquire(registerStatusLock);

                    while(theLocksInHomeDepotEntry._managerEmtpyingTill[currentRegister] != -1) {
                        /* Cash register hasn't started to work yet */
                        /* lets wait for it to startup */
                        #ifdef PRINT_EXTRA_LOG_CLERK
                            printf_3("CLERK# ", currentRegister, " :Cash regiter is busy, lets wait\n");
                        #endif
                        Acquire(peopleWaiting);
                        Release(registerStatusLock);
                        do
                        {
                        	theLocksInHomeDepotEntry._clerkWantsToInteract[currentRegister]=0;
                        	(theLocksInHomeDepotEntry._peopleWaitingForCashRegister[currentRegister])++;
                        	Wait(theLocksInHomeDepotEntry._peopleWaitingForCashRegiterCV[currentRegister],peopleWaiting);
                        }while(theLocksInHomeDepotEntry._clerkWantsToInteract[currentRegister] != 1);
                        theLocksInHomeDepotEntry._clerkWantsToInteract[currentRegister]=-1;
                        Acquire(registerStatusLock);
                        Release(peopleWaiting);
                    }
                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " :Let the register know we want to scan items\n");
                    #endif

                    Signal(theLocksInHomeDepotEntry._registerCV[currentRegister],registerStatusLock);

                    Acquire(registerLock);

                    theLocksInHomeDepotEntry._managerEmtpyingTill[currentRegister] = 0;

                    Release(registerStatusLock);

                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " :Lets wait for cash regiter until it is ready\n");
                    #endif

                    /* Lets wait for cash register till announce it is ready to accept items */
                    Wait(theLocksInHomeDepotEntry._fromRegisterInteractionCV[currentRegister], registerLock);
                    /* Register is ready to accept our items. */
                #endif

                totalItemsProvidedCount = 0;
                while(theLocksInHomeDepotEntry._nextItem[currentRegister] != NOT_FOUND) {
                    itemIdpassed = theLocksInHomeDepotEntry._nextItem[currentRegister];
                    totalItemsProvidedCount++;

                    #ifdef PRINT_OUTPUT_CLERK
                        printf_7("Clerk ", currentRegister, " got item [", itemIdpassed, "] from Customer [", theLocksInHomeDepotEntry._customerBeingHelped[currentRegister], "].\n");
                    #endif

                    #ifndef IGNORE_REGISTERS
                        /* Lets put the item into the cash register */
                        if(customerWantsToReturn == TRUE) {
                            theLocksInHomeDepotEntry._itemToRefund[currentRegister] = theLocksInHomeDepotEntry.itemsInStore[itemIdpassed];
                            theLocksInHomeDepotEntry._itemToScan[currentRegister]._itemId = NOT_FOUND;
                        } else {
                            theLocksInHomeDepotEntry._itemToScan[currentRegister] = theLocksInHomeDepotEntry.itemsInStore[itemIdpassed];
                            theLocksInHomeDepotEntry._itemToRefund[currentRegister]._itemId = NOT_FOUND;
                        }

                        Signal(theLocksInHomeDepotEntry._toRegisterInteractionCV[currentRegister], registerLock);

                        /* Lets wait for cash register till announces it is ready to accept items */
                        #ifdef PRINT_OUTPUT_CLERK
                            printf_7("Clerk ", currentRegister, " gave item [", itemIdpassed, "] to Cash Register [", currentRegister, "] for scanning.\n");
                        #endif

                        Wait(theLocksInHomeDepotEntry._fromRegisterInteractionCV[currentRegister], registerLock);
                    #endif

                    Signal(theLocksInHomeDepotEntry._customerCV[currentRegister], clerkLock);

                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, ": Lets wait for the next item.\n");
                    #endif

                    Wait(theLocksInHomeDepotEntry._clerkCV[currentRegister], clerkLock);

                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " : Customer just signaled that the next item is ready\n");
                    #endif
                }

                #ifdef PRINT_EXTRA_LOG_CLERK
                    if (totalItemsProvidedCount == 0) {
                        printf_3("CLERK# ", currentRegister, " : Customer hasn't passed any items.\n");
                    } else {
                        printf_3("CLERK# ", currentRegister, " : Customer has passed all of his items.\n");
                    }
                #endif
                #ifdef PRINT_EXTRA_LOG_CLERK
                    printf_3("CLERK# ", currentRegister, " : Get the totals from cash register\n");
                #endif

                /* Now we need to calculate the total */
                totalTransactionAmount = 0;

                #ifndef IGNORE_REGISTERS
                    theLocksInHomeDepotEntry._itemToRefund[currentRegister]._itemId = NOT_FOUND;
                    theLocksInHomeDepotEntry._itemToScan[currentRegister]._itemId = NOT_FOUND;

                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " signal cash register to give us the total\n");
                    #endif

                    Signal(theLocksInHomeDepotEntry._toRegisterInteractionCV[currentRegister], registerLock);

                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " wait for cash register till it calculates the total amount\n");
                    #endif

                    Wait(theLocksInHomeDepotEntry._fromRegisterInteractionCV[currentRegister], registerLock);

                    /* Grab the total from the cash register */
                    totalTransactionAmount = theLocksInHomeDepotEntry._moneyToTransfer[currentRegister];
                #endif

                #ifdef PRINT_OUTPUT_CLERK
                    printf_7("Clerk [", currentRegister, "] got total cost $[", totalTransactionAmount, "] from Cash Register [", currentRegister, "].\n");
                #endif

                /* Signal the customer to pay/receive the money */
                Signal(theLocksInHomeDepotEntry._customerCV[currentRegister], clerkLock);

                /* Wait till customer pays his bill or pickup his refund */
                #ifdef PRINT_OUTPUT_CLERK
                    printf_7("Clerk [", currentRegister, "] tells Customer [", theLocksInHomeDepotEntry._customerBeingHelped[currentRegister], "] total cost is $[", totalTransactionAmount, "].\n");
                #endif

                Wait(theLocksInHomeDepotEntry._clerkCV[currentRegister], clerkLock);

                /* Pickup the money paid */
                #ifdef PRINT_OUTPUT_CLERK
                    if (customerWantsToReturn) {
                            printf_7("Clerk [", currentRegister, "] got money [", totalTransactionAmount, "] from Cash Register [", currentRegister, "].\n");
                    } else {
                            printf_7("Clerk [", currentRegister, "] got money [", totalTransactionAmount, "] from Customer [", theLocksInHomeDepotEntry._customerBeingHelped[currentRegister], "].\n");
                    }
                #endif

                /* Wait for the receipt from the cash register */
                #ifndef IGNORE_REGISTERS
                    /* Let the cash register know the amount was processed */
                    /* and receipt is required */
                    Signal(theLocksInHomeDepotEntry._toRegisterInteractionCV[currentRegister], registerLock);

                    #ifdef PRINT_OUTPUT_CLERK
                        if (customerWantsToReturn) {
                            printf_7("Clerk [", currentRegister, "] gave money [", totalTransactionAmount, "] to Customer [", theLocksInHomeDepotEntry._customerBeingHelped[currentRegister], "].\n");
                        } else {
                            printf_7("Clerk [", currentRegister, "] gave money [", totalTransactionAmount, "] to Cash Register [", currentRegister, "].\n");
                        }
                    #endif

                    Wait(theLocksInHomeDepotEntry._fromRegisterInteractionCV[currentRegister], registerLock);

                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_5("Clerk [", currentRegister, "] got receipt from Cash Register[", currentRegister, "].\n");
                    #endif
                #endif

                /* And let the customer know he can pickup the receipt */
                Signal(theLocksInHomeDepotEntry._customerCV[currentRegister], clerkLock);

                /* Wait till customer pickup the receipt (and payment if it was a refund) */
                #ifdef PRINT_OUTPUT_CLERK
                    printf_5("Clerk [", currentRegister, "] gave the receipt to Customer [", theLocksInHomeDepotEntry._customerBeingHelped[currentRegister], "] and tells him to leave.\n");
                #endif
                #ifdef PRINT_EXTRA_LOG_CLERK
                    printf_3("CLERK# ", currentRegister, " : Wait till customer picks up the receipt\n");
                #endif

                Wait(theLocksInHomeDepotEntry._clerkCV[currentRegister], clerkLock);

                /* We are done with this customer, lets release our lock */
                #ifdef PRINT_EXTRA_LOG_CLERK
                    printf_3("CLERK# ", currentRegister, " : Done with this customer!!!\n");
                #endif

                #ifndef IGNORE_REGISTERS
                    #ifdef PRINT_EXTRA_LOG_CLERK
                        printf_3("CLERK# ", currentRegister, " :Release the register lock\n");
                    #endif

                    Release(registerLock);
                #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLock->Release()!\n");
            #endif

            Release(clerkLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLock->Release() Done!\n");
            #endif

            clerk.haveAJob = TRUE;
        } else {
            /* time to go on break - all clerks are allowed to go on break */
            #ifdef PRINT_OUTPUT_CLERK
                printf_5("Clerk [", currentRegister, "] is leaving Cash Register [", currentRegister, "] and going on break.\n");
            #endif

            /* set our status to on break */
            theLocksInHomeDepotEntry._clerkStatus[currentRegister] = CLERK_BREAK;

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLineLock->Release()!\n");
            #endif

            Release(theLocksInHomeDepotEntry._clerkLinelock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkLineLock->Release() Done!\n");
            #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkOnBreakLock->Acquire()!\n");
            #endif

            /* get the going on break lock */
            Acquire(theLocksInHomeDepotEntry._clerksOnBreakLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkOnBreakLock->Acquire() Done!\n");
            #endif

            /* increment the number of clerks on break */
            theLocksInHomeDepotEntry._clerksOnBreakMV ++;

            /* wait to be woken up */
            #ifdef PRINT_EXTRA_LOG_CLERK
                printf_3("CLERK# ", currentRegister, " : Wait for manager to wake me up\n");
            #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerksOnBreakCV->Wait()\n");
            #endif

            Wait(theLocksInHomeDepotEntry._clerksOnBreakCV, theLocksInHomeDepotEntry._clerksOnBreakLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerksOnBreakCV->Wait() Done!\n");
            #endif

            /* we're no longer on break, lets get busy! */
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerksLineLock->Acquire()\n");
            #endif

            Acquire(theLocksInHomeDepotEntry._clerkLinelock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerksLineLock->Acquire() Done!\n");
            #endif

            theLocksInHomeDepotEntry._clerkStatus[currentRegister] = CLERK_BUSY;

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerksLineLock->Release()\n");
            #endif

            Release(theLocksInHomeDepotEntry._clerkLinelock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerksLineLock->Release done!()\n");
            #endif
            #ifdef PRINT_OUTPUT_CLERK
                printf_3("Clerk [", currentRegister, "] was called by Manager to work.\n");
            #endif

            waitForClerksToReturn = theLocksInHomeDepotEntry._customerNeedsClerkAssitanceLock;

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " ClerkOnBreakLock->Release()\n");
            #endif

            Release(theLocksInHomeDepotEntry._clerksOnBreakLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " clerkOnBreakLock->Release() done!\n");
            #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " waitForClerksToReturn->Acquire()\n");
            #endif

            Acquire(waitForClerksToReturn);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CLERK# ", currentRegister, " waitForClerksToReturn->Acquire() Done!\n");
            #endif

            /* See if any customer was waiting for us */
            #ifdef PRINT_EXTRA_LOG_CLERK
                printf_3("CLERK# ", currentRegister, " : Just got back from break.\n");
            #endif

            totalSignaled = 0;
            while (theLocksInHomeDepotEntry._customersNeedingAsistanceMV > 0 && totalSignaled < 5) {
                Signal(theLocksInHomeDepotEntry._customerNeedsClerkAssitanceCV, waitForClerksToReturn);
                theLocksInHomeDepotEntry._customersNeedingAsistanceMV --;
                theLocksInHomeDepotEntry._clerkIDSignalingToCustomer = currentRegister;
                totalSignaled++;

                #ifdef PRINT_EXTRA_LOG_CLERK
                    printf_3("CLERK# ", currentRegister, " : Just signaled a client to wake up.\n");
                #endif

                Wait(theLocksInHomeDepotEntry._customerShouldFindTheClerkCV, waitForClerksToReturn);

                #ifdef PRINT_EXTRA_LOG_CLERK
                    printf_3("CLERK# ", currentRegister, " : client answered back that he is in line\n");
                #endif
            }
            #ifdef PRINT_EXTRA_LOG_CLERK
                printf_5("CLERK# ", currentRegister, " : Just signaled ", totalSignaled, " customers to come into line\n");
            #endif

            Release(waitForClerksToReturn);
        }
    }
}


#endif /* USERCLERK_H_ */
